package dcpb.re.encryption;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Writer;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.*;
import java.util.AbstractMap.SimpleEntry;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;

import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.plaf.jpbc.field.curve.CurveElement;
import it.unisa.dia.gas.plaf.jpbc.field.z.ZrElement;
import it.unisa.dia.gas.plaf.jpbc.pairing.PairingFactory;

public class ProxyReEncrypt {

	public ProxyReEncrypt() {
		// TODO Auto-generated constructor stub
	}
	public SimpleEntry<PubParams,Element> setup() {
		//˫����Ⱥ�ĳ�ʼ����jPBC�о��Ƕ�Pairing����ĳ�ʼ��
		PubParams p=new PubParams();
		p.pairing = PairingFactory.getPairing("resource/a.properties");
		/* Return G1 */
		p.G1 = p.pairing.getG1();//��ЩҪ����ͬһ���ռ����ͬ�Ľڵ���ι���

		/* Return G2 */
		p.G2 = p.pairing.getG2();

		/* Return GT */
		p.GT = p.pairing.getGT();
		/* Return Zr */
		p.Zr = p.pairing.getZr();
		p.one=(ZrElement) p.Zr.newOneElement();
		p.g = (CurveElement) p.G1.newRandomElement().getImmutable();
		p.h = (CurveElement) p.G2.newRandomElement().getImmutable();
		Element MSK=(ZrElement) p.Zr.newRandomElement().getImmutable();
		p.p_pub=p.g.duplicate().mulZn(MSK.duplicate());
		return new SimpleEntry<>(p,MSK);
		
	}
	public void save(PubParams p) {
		writeFile("resource/g",p.g.toBytes());
		writeFile("resource/h",p.h.toBytes());
		writeFile("resource/ppub",p.p_pub.toBytes());
	}
	public PubParams read() throws IOException {
		PubParams p=new PubParams();
		p.pairing = PairingFactory.getPairing("resource/a.properties");
		/* Return G1 */
		p.G1 = p.pairing.getG1();//��ЩҪ����ͬһ���ռ����ͬ�Ľڵ���ι���

		/* Return G2 */
		p.G2 = p.pairing.getG2();

		/* Return GT */
		p.GT = p.pairing.getGT();
		/* Return Zr */
		p.Zr = p.pairing.getZr();
		p.one=(ZrElement) p.Zr.newOneElement();		
		p.g=(CurveElement) p.G1.newElementFromBytes(readFile("resource/g"));
		p.h=(CurveElement) p.G2.newElementFromBytes(readFile("resource/h"));
		p.p_pub=(CurveElement) p.G2.newElementFromBytes(readFile("resource/ppub"));
		return p;
	   
	}
	
	public SimpleEntry<Element,Element> KeyGen(PubParams pubParams,Element MSK,Element ID_i) {
		ZrElement hID_i=(ZrElement)pubParams.hash(ID_i).duplicate();
		ZrElement tmp=hID_i.duplicate().div(MSK.duplicate().add(hID_i));
		Element sk_i1=pubParams.g.duplicate().mulZn(tmp);
		tmp=pubParams.one.duplicate().div(MSK.duplicate().add(hID_i));
		Element sk_i2=pubParams.h.duplicate().mulZn(tmp);
		return new SimpleEntry<>(sk_i1, sk_i2);
	}
	
	public SimpleEntry<Map,Map> Enc(PubParams pubParams,Element M,Element w) {
		ZrElement x=(ZrElement) pubParams.Zr.newRandomElement();
		 ZrElement y=(ZrElement) pubParams.Zr.newRandomElement();
		 ZrElement a=(ZrElement) pubParams.Zr.newRandomElement();
		 ZrElement b=(ZrElement) pubParams.Zr.newRandomElement();
		//e����
		Element out=pubParams.pairing.pairing(pubParams.g.duplicate().mulZn(a),pubParams.h.duplicate().mulZn(b));
		 byte[] c1=pubParams.xor(M.toBytes(),pubParams.permutationFunction(out).toBytes());
		 Element c2=b.duplicate().mul(x);
		ZrElement zr=a.duplicate().mul(y);
		 Element c3= pubParams.h.duplicate().mulZn(zr);
		 Map<String,Element> sita=new HashMap<>();
		 sita.put("x", x);
		 sita.put("y", y);
		 sita.put("a", a);
		 sita.put("b", b);
		 sita.put("w", w);
		 Map<String,Object> C=new HashMap<>();
		 C.put("c1", c1);
		 C.put("c2", c2);
		 C.put("c3", c3);
		 return new SimpleEntry<>(sita, C);
		 
		 
	}
	public SimpleEntry RkGen(PubParams p,Element w,Map<String,Element> sita,Element A) {
		CurveElement rk_pre=p.p_pub.duplicate().mulZn(A.duplicate());
		Element rkw_1=rk_pre.mulZn(sita.get("a").duplicate().mul(sita.get("x").duplicate().invert()));
		Element rkw_2=sita.get("b").duplicate().mul(sita.get("y").duplicate().invert());
		return new SimpleEntry<>(rkw_1,rkw_2);
		  
	}
	public SimpleEntry<List<Element>, List<Element>> getSecret(Element secret,int m,int t,PubParams p) {
		Element[] f=new Element[t];
		  f[0]=secret;
		  for(int i=1;i<t;i++) {//t
		    f[i]=p.Zr.newRandomElement();
		  }
		  List<Element> ys=new ArrayList<Element>();
		  List<Element> xs=new ArrayList<Element>();
		  for(int j=0;j<m;j++) {//m*
			   Element id=p.Zr.newRandomElement();
			   ys.add(fun(f,id));
			   xs.add(id);
		  }
		  return new SimpleEntry<>(xs,ys);
	}
	public List<BigInteger[]> getSecret2(BigInteger secret, int n, PubParams p) {
		List<BigInteger[]> ret= new LinkedList<>();
		BigInteger[] f=new BigInteger[n+1];
		BigInteger m0=new BigInteger("3");//p.Zr.newRandomElement().toBigInteger();//public
		//5个互质素数
		f[0]=new BigInteger("11");
		f[1]=new BigInteger("13");
		f[2]=new BigInteger("17");
		f[3]=new BigInteger("19");
//		for(int i=0;i<n;i++) {
//			f[i]=p.Zr.newRandomElement().toBigInteger();
//		}
		f[n]=m0;
		BigInteger afa=new BigInteger("51");//p.Zr.newRandomElement().toBigInteger();
		BigInteger first=secret.add(afa.multiply(m0));


		BigInteger[] s=new BigInteger[n];
		for(int i=0;i<n;i++){//有n个秘密
			s[i]=first.mod(f[i]);
		}
		ret.add(f);
		ret.add(s);
		return ret;
	}
	public Map<String,Object> ReEnc(Map<String,Object> C,Element rkw_1,Element rkw_2) {
		//Element c31=((Element)C.get("c3")).duplicate().mulZn(rkw_2.duplicate());
		 Map<String,Object> C1=new HashMap<>();
		 C1.put("c1", C.get("c1"));
		 C1.put("c21", rkw_1.duplicate().mulZn((Element)C.get("c2")));
		 C1.put("c31", ((Element)C.get("c3")).duplicate().mulZn(rkw_2.duplicate()));
		return C1;
	}
	public byte[] Dec1(PubParams p,Map<String,Element> sita,Map<String,Object> C) {
		Element D=p.pairing.pairing(p.g.duplicate().mulZn(sita.get("a").duplicate()), p.h.duplicate().mulZn(sita.get("b").duplicate()));
		byte[] M=p.xor((byte[])C.get("c1"),p.permutationFunction(D.duplicate()).toBytes());
		return M;
	}
	public byte[] Dec2(PubParams p,int t,
			   			byte[] c1,
			   			Element c21,
			   			SimpleEntry<List<Element>,List<Element>> c31XsAndYs,
			         SimpleEntry<Element,Element> sk,
			         Element sk_r) {
		
		Element c31=recover(p,c31XsAndYs.getKey(), c31XsAndYs.getValue(), t);
		Element tmp1=p.pairing.pairing(c21, sk_r);
		Element tmp2=p.pairing.pairing(sk.getKey(),c31);
		return p.xor(c1,p.permutationFunction(tmp1.duplicate().mul(tmp2).duplicate()).toBytes());
	}
	public Element dec3(PubParams p,List<Element> P,List<Element> mu,Element id,Element sk2) {
		Element temp=sk2;
		for(int i=0;i<P.size();i++) {
			if(id.equals(mu.get(i))) return null;
			Element t=p.one.duplicate().div(id.duplicate().sub(mu.get(i)));
			temp=t.mul(P.get(i).duplicate().sub(temp));
		}
		return temp;
	}
	
	public Element recover(PubParams p, List<Element> xs,List<Element> ys,int t) {
		Element sum=p.G1.newZeroElement();
		  for(int i=0;i<t;i++) {
		   Element xtemp=p.Zr.newOneElement();
		   for(int j=0;j<t;j++) {
		    if(j==i) continue;
		    xtemp=xtemp.mul(xs.get(j).duplicate().div(xs.get(j).duplicate().sub(xs.get(i))));
		   }
		   sum=sum.duplicate().add(ys.get(i).duplicate().mulZn(xtemp));
		  }
		  return sum;
		
	}
	public BigInteger recover2(BigInteger[] s,BigInteger[] m,int t) {
		BigInteger M=new BigInteger("1");
		for(int i=0;i<t;i++){//取前三个
			M=M.multiply(m[i]);
		}
		BigInteger[] e=new BigInteger[t];
		for(int i=0;i<t;i++){
			e[i]=s[i].multiply(M).divide(m[i]);
		}
		BigInteger x0=new BigInteger("0");
		for(int i=0;i<t;i++){
			x0=x0.add(s[i].multiply(e[i]));
		}
		return x0.mod(M).mod(m[m.length-1]);
	}
	
	public Element fun(Element[] f,Element x) {
		Element sum=f[0].duplicate();
		for(int i=1;i<f.length;i++) {
			
			sum=sum.duplicate().add(f[i].duplicate().mul(pow(x,i)));
		}
		return sum;
	}
	public Element pow(Element x,Integer t) {
		
		Element ret=x.duplicate();
		for(int i=1;i<t;i++) {
			ret=ret.duplicate().mul(x);
		}
		return ret;
	}

	public   byte[] encrypt(byte[] datasource, byte[] password) {     
		long startTime = System.currentTimeMillis();    //��ȡ��ʼʱ��
        try{
        SecureRandom random = new SecureRandom();
        DESKeySpec desKey = new DESKeySpec(password);
        //����һ���ܳ׹�����Ȼ��������DESKeySpecת����
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(desKey);
        //Cipher����ʵ����ɼ��ܲ���
        Cipher cipher = Cipher.getInstance("DES");
        //���ܳ׳�ʼ��Cipher����
        cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
        //���ڣ���ȡ���ݲ�����
        //��ʽִ�м��ܲ���
        byte[] bs=cipher.doFinal(datasource);
        long endTime = System.currentTimeMillis();    //��ȡ����ʱ��
		System.out.println("DES encrypt��������ʱ�䣺" + (endTime - startTime) + "ms");    //�����������ʱ��
        return bs;
        }catch(Throwable e){
                e.printStackTrace();
        }
        return null;
}
    /**
     * ����
     * @param src byte[]
     * @param password String
     * @return byte[]
     * @throws Exception
     */
    public  byte[] decrypt(byte[] src, byte[] password) throws Exception {
    	long startTime = System.currentTimeMillis();    //��ȡ��ʼʱ��
            // DES�㷨Ҫ����һ�������ε������Դ
            SecureRandom random = new SecureRandom();
            // ����һ��DESKeySpec����
            DESKeySpec desKey = new DESKeySpec(password);
            // ����һ���ܳ׹���
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            // ��DESKeySpec����ת����SecretKey����
            SecretKey securekey = keyFactory.generateSecret(desKey);
            // Cipher����ʵ����ɽ��ܲ���
            Cipher cipher = Cipher.getInstance("DES");
            // ���ܳ׳�ʼ��Cipher����
            cipher.init(Cipher.DECRYPT_MODE, securekey, random);
            byte[] bs=cipher.doFinal(src);
            long endTime = System.currentTimeMillis();    //��ȡ����ʱ��

    		System.out.println("DES decrypt��������ʱ�䣺" + (endTime - startTime) + "ms");    //�����������ʱ��
          
            // ������ʼ���ܲ���
            return bs;
        }
    
    public static void writeFile(String path,byte[] content) {
    	
    	long startTime = System.currentTimeMillis();    //��ȡ��ʼʱ��
    	File f=new File(path);
    	FileOutputStream fos;
		try {
			fos = new FileOutputStream(f);
			fos.write(content);
			fos.close();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		long endTime = System.currentTimeMillis();    //��ȡ����ʱ��

		//System.out.println("д��������ʱ�䣺" + (endTime - startTime) + "ms");    //�����������ʱ��
		Demo.time2+=(endTime - startTime);
				
	}
	public static byte[] readFile(String filePath) throws IOException {
		long startTime = System.currentTimeMillis();    //��ȡ��ʼʱ��
		File file = new File(filePath);  
        long fileSize = file.length();  
//        if (fileSize > Integer.MAX_VALUE) {  
//        	logger.info("file too big...");  
//            return null;  
//        }  
        FileInputStream fi = new FileInputStream(file);  
        byte[] buffer = new byte[(int) fileSize];  
        int offset = 0;  
        int numRead = 0;  
        while (offset < buffer.length  
        && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {  
            offset += numRead;  
        }  
        // ȷ���������ݾ�����ȡ  
        if (offset != buffer.length) {  
        	throw new IOException("Could not completely read file "  
                    + file.getName());  
        }  
        fi.close();
        long endTime = System.currentTimeMillis();    //��ȡ����ʱ��

		//System.out.println("���ļ���������ʱ�䣺" + (endTime - startTime) + "ms");    //�����������ʱ��
		Demo.time2+=(endTime - startTime);
        return buffer;
		
	}
	
}
